#ifndef CAMERA_HPP
#define CAMERA_HPP

#include "../math/ray.hpp"
#include "../math/vec_operation.hpp"

/////////////////////////
/// 使用右手坐标系
/////////////////////////

template <ui32 width, ui32 height>
struct BaseCamera {
  vec3f eye;
  f32 s;
  vec3f up() const { return v; } // camera up vector

  vec3f right() const { return u; }
  vec3f forward() const { return w; }

  // uvw basis camera coordinate system
  vec3f u;
  vec3f v;
  vec3f w;

  vec3f across, corner, bup;

  BaseCamera( f32 dis, vec3f e, vec3f t, vec3f vup = vec3f( 0, 1, 0 ) )
      : s( dis ), eye( e ) {
    // Calculate camera look direction
    w = normalize( e - t );
    // Calculate the right vector
    u = normalize( cross( w, vup ) );
    // Calculate the up vector
    v = normalize( cross( u, w ) );

    across = width * v;
    bup = height * u;
    corner = -across / 2 - bup / 2 - s * w + e;
  }
};

/*
 * @brief 一个简单的Camera
 * @note 默认使用的向上向量为(0, 1, 0)
 */
template <ui32 width, ui32 height>
class AxisAlignedCamera : public BaseCamera<width, height> {
public:
  Ray ray( vec2f val ) {
    // caculate view-plane bottom left corner
    vec3f sc = normalize( val.x * across + val.y * bup + corner );
    Ray r( eye, normalize( sc - eye ) );
    return r;
  }

  AxisAlignedCamera( f32 dis, vec3f e, vec3f t, vec3f vup = vec3f( 0, 1, 0 ) )
      : BaseCamera( dis, e, t, vup ) {}
};

template <ui32 width, ui32 height>
class ThinLensCamera : public BaseCamera<width, height> {
public:
  f32 lens_radius;

  ThinLensCamera( f32 apeture, f32 dis, vec3f e, vec3f t,
                  vec3f vup = vec3f( 0, 1, 0 ) )
      : BaseCamera( dis, e, t, vup ) {
    lens_radius = apeture / 2.0f;
  }

  Ray ray( vec2f val, vec2f lensSample ) {
    // calculate square shaped lens
    vec3f orgin = eye + 2.f * ( lensSample.x - .5f ) * lens_radius * u +
                  2.f * ( lensSample.y - .5f ) * lens_radius * v;
    vec3f sc = corner + val.x * across + val.y * bup;
    Ray r( orgin, normalize( sc - orgin ) );
    return r;
  }
};

#endif